package com.teradata.olingo.metadata.api;

import com.teradata.olingo.metadata.core.edm.mapper.api.GenericServiceDocument;
import com.teradata.olingo.metadata.core.edm.mapper.exception.ODataGenericException;
import com.teradata.olingo.metadata.core.edm.mapper.impl.GenericEdmNameBuilder;
import com.teradata.olingo.metadata.core.edm.mapper.impl.GenericServiceDocumentFactory;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.provider.*;
import org.apache.olingo.commons.api.edmx.EdmxReference;
import org.apache.olingo.commons.api.ex.ODataException;

import java.util.Enumeration;
import java.util.List;
import java.util.Locale;

public class GenericEdmProvider extends CsdlAbstractEdmProvider {

  private final GenericEdmNameBuilder nameBuilder;
  private final GenericServiceDocument serviceDocument;

  // http://docs.oasis-open.org/odata/odata/v4.0/errata02/os/complete/part3-csdl/odata-v4.0-errata02-os-part3-csdl-complete.html#_Toc406397930
  public GenericEdmProvider(final String namespace,
                            final GenericEdmMetadataPostProcessor postProcessor, final String[] packageName) throws ODataException {
    super();
    this.nameBuilder = new GenericEdmNameBuilder(namespace);
    serviceDocument = new GenericServiceDocumentFactory(namespace, postProcessor, packageName)
        .getServiceDocument();
  }

  @Override
  public CsdlComplexType getComplexType(final FullQualifiedName complexTypeName) throws ODataException {
    for (final CsdlSchema schema : serviceDocument.getAllSchemas()) {
      if (schema.getNamespace().equals(complexTypeName.getNamespace())
          || schema.getAlias() != null && schema.getAlias().equals(complexTypeName.getNamespace())) {
        return schema.getComplexType(complexTypeName.getName());
      }
    }
    return null;
  }

  @Override
  public CsdlEntityContainer getEntityContainer() throws ODataException {
    return serviceDocument.getEdmEntityContainer();
  }

  @Override
  public CsdlEntityContainerInfo getEntityContainerInfo(final FullQualifiedName entityContainerName)
      throws ODataException {
    // This method is invoked when displaying the Service Document at e.g.
    // .../DemoService.svc
    if (entityContainerName == null
        || entityContainerName.equals(nameBuilder.buildFQN(nameBuilder.buildContainerName()))) {
      final CsdlEntityContainerInfo entityContainerInfo = new CsdlEntityContainerInfo();
      entityContainerInfo.setContainerName(nameBuilder.buildFQN(nameBuilder.buildContainerName()));
      return entityContainerInfo;
    }
    return null;
  }

  @Override
  public CsdlEntitySet getEntitySet(final FullQualifiedName entityContainerFQN, final String entitySetName)
      throws ODataException {
    final CsdlEntityContainer container = serviceDocument.getEdmEntityContainer();
    if (entityContainerFQN.equals(nameBuilder.buildFQN(container.getName()))) {
      return container.getEntitySet(entitySetName);
    }
    return null;
  }

  @Override
  public CsdlEntityType getEntityType(final FullQualifiedName entityTypeName) throws ODataException {

    for (final CsdlSchema schema : serviceDocument.getEdmSchemas()) {
      if (schema.getNamespace().equals(entityTypeName.getNamespace())) {
        return schema.getEntityType(entityTypeName.getName());
      }
    }
    return null;
  }

  @Override
  public CsdlFunctionImport getFunctionImport(final FullQualifiedName entityContainerFQN,
      final String functionImportName) throws ODataException {
    final CsdlEntityContainer container = serviceDocument.getEdmEntityContainer();
    if (entityContainerFQN.equals(nameBuilder.buildFQN(container.getName()))) {
      return container.getFunctionImport(functionImportName);
    }
    return null;
  }

  @Override
  public List<CsdlFunction> getFunctions(final FullQualifiedName functionName) throws ODataException {
    for (final CsdlSchema schema : serviceDocument.getEdmSchemas()) {
      if (schema.getNamespace().equals(functionName.getNamespace())) {
        return schema.getFunctions(functionName.getName());
      }
    }
    return null; // NOSONAR see documentation
  }

  @Override
  public List<CsdlAction> getActions(final FullQualifiedName actionName) throws ODataException {
    for (final CsdlSchema schema : serviceDocument.getEdmSchemas()) {
      if (schema.getNamespace().equals(actionName.getNamespace())) {
        return schema.getActions(actionName.getName());
      }
    }
    return null; // NOSONAR see documentation
  }

  @Override
  public CsdlActionImport getActionImport(final FullQualifiedName entityContainerFQN, final String actionImportName)
      throws ODataException {
    final CsdlEntityContainer container = serviceDocument.getEdmEntityContainer();
    if (entityContainerFQN.equals(nameBuilder.buildFQN(container.getName()))) {
      return container.getActionImport(actionImportName);
    }
    return null;
  }

  @Override
  public CsdlEnumType getEnumType(final FullQualifiedName enumTypeNameFQN) throws ODataException {

    for (final CsdlSchema schema : serviceDocument.getEdmSchemas()) {
      if (schema.getNamespace().equals(enumTypeNameFQN.getNamespace())) {
        return schema.getEnumType(enumTypeNameFQN.getName());
      }
    }
    return null;
  }

  @Override
  public CsdlAnnotations getAnnotationsGroup(final FullQualifiedName targetName, String qualifier)
      throws ODataException {
    return null;
  }

  @Override
  public CsdlTerm getTerm(final FullQualifiedName termName) throws ODataException {
    return serviceDocument.getTerm(termName);
  }

  @Override
  public CsdlTypeDefinition getTypeDefinition(final FullQualifiedName typeDefinitionName) throws ODataException {
    for (final CsdlSchema schema : serviceDocument.getAllSchemas()) {
      if (schema.getNamespace().equals(typeDefinitionName.getNamespace())) {
        return schema.getTypeDefinition(typeDefinitionName.getName());
      }
    }
    return null;
  }

  @Override
  public List<CsdlSchema> getSchemas() throws ODataException {
    return serviceDocument.getEdmSchemas();
  }

  public GenericServiceDocument getServiceDocument() {
    return serviceDocument;
  }

  public void setRequestLocales(final Enumeration<Locale> locales) {
    ODataGenericException.setLocales(locales);
  }

  public List<EdmxReference> getReferences() {
    return serviceDocument.getReferences();
  }

}
